package com.glink.common.util.code;


import com.glink.common.util.EncoUtils;
import com.google.common.collect.Lists;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wg
 */
public class ParseDbToNewConstantsClass {

    private static final String CLASS_NAME_PREFIX = "CodeType_";
    private static final String VALUE_PREFIX = "KEY_";

    private static Connection conn;
    private static ResultSet resultSet1;
    private static ResultSet resultSet2;
    private static PreparedStatement stat1;
    private static PreparedStatement stat2;
    private static String HOST = null;
    private static String USER = null;
    private static String PWD = null;
    private static String PKG = null;
    private static String PORT = null;
    private static String DB_NAME = null;
    private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
    private static final String SQL_TYPE = "SELECT id, type, parent_type as parentType, `key` as myKey, name as keyName FROM `center_code` where is_delete = '0' and is_leaf = '0' order by seq, type, id";
    private static final String SQL_DETAILS = "select id, type, parent_type as parentType, `key` as myKey, name as keyName from `center_code` where is_delete = '0' order by seq, type, id";
    private static final List<MainCodeType> allCodes = new ArrayList<>();


    public static void main(String[] args) {
        try {
            allCodes.clear();
            args = EncoUtils.parseFromStr(args[0]);
            ParseDbToNewConstantsClass.parseDbToNewConstantsClass(args);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 操作步骤
     * 将数据库信息拼字符串： String str = "192.168.1.169|root|4r5t6y$R%T^Y|13406|school_bak|com.glink.utils(生成的文件会放在这个包下)";
     * 对 str 使用  PBEWithMD5AndDES 进行加密
     */
    @SuppressWarnings("unused")
    private static void parseDbToNewConstantsClass(String[] args) throws Exception {
        conn = null;
        try {
            conn = getConn(args);
            getAllCodesFromDb();
            generateClass();
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception();
        } finally {
            close();

        }
    }

    private static File createParentDir() {
        File file = new File(packageToPath());
        File dir = file.getParentFile();
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return file;
    }

    private static void generateClass() throws IOException, TemplateException {
        //创建Configuration对象
        Configuration configuration = new Configuration();
        //设置模板所在目录
        configuration.setDirectoryForTemplateLoading(new File("common/src/main/java/com/glink/common"));
        //获取模板
        Template template = configuration.getTemplate("BaseCodeConstants.ftl", "GBK");
        //设置数据并执行
        Map map = new HashMap();

        map.put("itemClasses", allCodes);
//        map.put("generateDate", (new Date()).toString());
        File pathDir = createParentDir();

        Writer writer = new OutputStreamWriter(new FileOutputStream(pathDir), StandardCharsets.UTF_8);
        template.process(map, writer);

        System.out.println("完成");
    }

    private static String packageToPath() {
        StringBuilder sb = new StringBuilder();
        sb.append(new File("").getAbsolutePath());
        if(!sb.toString().endsWith("common")){
            sb.append("/common");
        }
        sb.append("/src/main/java/");
        sb.append(PKG.replace(".", "/"));
        sb.append("/BaseCodeConstants.java");
       
        return sb.toString().replace("\\", "/");
    }

    private static Connection getConn(String[] args) throws Exception {
        HOST = args[0];
        if (StringUtils.isEmpty(HOST)) {
            throw new Exception("host cannot be null");
        }

        USER = args[1];
        if (StringUtils.isEmpty(USER)) {
            throw new Exception("user cannot be null");
        }

        PWD = args[2];
        if (StringUtils.isEmpty(PWD)) {
            throw new Exception("pwd cannot be null");
        }

        PORT = args[3];
        if (StringUtils.isEmpty(PORT)) {
            throw new Exception("port cannot be null");
        }

        DB_NAME = args[4];
        if (StringUtils.isEmpty(DB_NAME)) {
            throw new Exception("dbName cannot be null");
        }

        PKG = args[5];
        if (StringUtils.isEmpty(PKG)) {
            throw new Exception("package cannot be null");
        }

        return getConnection();
    }

    private static Connection getConnection() throws Exception {
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            throw e;
        }
        return DriverManager.getConnection("jdbc:mysql://" + HOST + ":" + PORT + "/" + DB_NAME + "?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false", USER, PWD);
//        return DriverManager.getConnection("jdbc:sqlserver://" + HOST + ":" + PORT + ";DatabaseName=" + DB_NAME, USER, PWD);
    }

    private static void getAllCodesFromDb() throws Exception {
        resultSet1 = getMainResult();
        resultSet2 = getItemResult();
        List<ItemCodeType> detailCodes = disposeData();
        List<String> typeList = Lists.newArrayList();

        while (resultSet1.next()) {
            String childType = resultSet1.getString("type");
            List<ItemCodeType> itemCodes = extract(detailCodes, childType);
            
            if(CollectionUtils.isNotEmpty(itemCodes)){
                itemCodes.forEach( itemCodeType -> {
                    List<ItemCodeType> extract = extract(detailCodes, itemCodeType.getType());
                    if(CollectionUtils.isNotEmpty(extract)){
                        MainCodeType mainCode = new MainCodeType(
                                CLASS_NAME_PREFIX + itemCodeType.getType(),
                                itemCodeType.getTypeCode(),
                                itemCodeType.getValueOfCn() + " 类别 " + childType, extract);
                        if(!typeList.contains(mainCode.getMainClassName())) {
                            allCodes.add(mainCode);
                            typeList.add(mainCode.getMainClassName());
                        }
                    }
                });

                MainCodeType mainCode = new MainCodeType(
                        CLASS_NAME_PREFIX + resultSet1.getString("type"),
                        childType,
                        resultSet1.getString("keyName") + " 类别 " + childType, itemCodes);
                if(!typeList.contains(mainCode.getMainClassName())){
                    allCodes.add(mainCode);
                    typeList.add(mainCode.getMainClassName());
                }
               
            }
           
        }
    }

    private static List<ItemCodeType> disposeData() throws SQLException {
        List<ItemCodeType> itemCodeTypes = new ArrayList<>();
        while (resultSet2.next()) {
            ItemCodeType mainCode = new ItemCodeType(
                    VALUE_PREFIX + resultSet2.getString("type"),
                    resultSet2.getString("myKey"),
                    resultSet2.getString("keyName"),
                    resultSet2.getString("type"),
                    resultSet2.getString("parentType"));
            itemCodeTypes.add(mainCode);
        }
        return itemCodeTypes;
    }

    private static List<ItemCodeType> extract(List<ItemCodeType> itemCodeTypes, final String codeType) {
        return itemCodeTypes.stream().filter(item ->
                codeType.equals(item.getParentType())).
                collect(Collectors.toList());
    }

    private static void close() {
        if (Objects.nonNull(resultSet1)) {
            try {
                resultSet1.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (Objects.nonNull(resultSet2)) {
            try {
                resultSet2.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (Objects.nonNull(stat1)) {
            try {
                stat1.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }


        if (Objects.nonNull(stat2)) {
            try {
                stat2.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }


        if (Objects.nonNull(conn)) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private static ResultSet getMainResult() throws Exception {
        return getMainStatement().executeQuery();
    }

    private static ResultSet getItemResult() throws Exception {
        return getItemStatement().executeQuery();
    }

    private static PreparedStatement getMainStatement() throws SQLException {
        stat1 = conn.prepareStatement(SQL_TYPE);
        return stat1;
    }

    private static PreparedStatement getItemStatement() throws SQLException {
        stat2 = conn.prepareStatement(SQL_DETAILS);
        return stat2;
    }

}